package com.ruoyi.system.service.impl;

import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.core.domain.PageQuery;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.ruoyi.common.utils.redis.RedisUtils;
import com.ruoyi.common.utils.spring.SpringUtils;
import com.ruoyi.system.Repository.EventRepository;
import com.ruoyi.system.domain.HiasEventClerk;
import com.ruoyi.system.domain.HiasEventTag;
import com.ruoyi.system.domain.bo.HiasEventClerkBo;
import com.ruoyi.system.domain.bo.HiasEventDelayBo;
import com.ruoyi.system.domain.to.HiasEventHotTO;
import com.ruoyi.system.domain.to.HiasEventTO;
import com.ruoyi.system.domain.vo.HiasEventHotVo;
import com.ruoyi.system.entity.Clerk;
import com.ruoyi.system.entity.Event;
import com.ruoyi.system.mapper.HiasClerkMapper;
import com.ruoyi.system.mapper.HiasEventClerkMapper;
import com.ruoyi.system.mapper.HiasEventTagMapper;
import com.ruoyi.system.message.HiasEventBaseMessage;
import com.ruoyi.system.message.impl.HiasEventDelayMessage;
import lombok.RequiredArgsConstructor;
import org.redisson.api.RLock;
import org.redisson.api.RReadWriteLock;
import org.redisson.api.RedissonClient;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import com.ruoyi.system.domain.bo.HiasEventBo;
import com.ruoyi.system.domain.vo.HiasEventVo;
import com.ruoyi.system.domain.HiasEvent;
import com.ruoyi.system.mapper.HiasEventMapper;
import com.ruoyi.system.service.IHiasEventService;

import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * 【请填写功能名称】Service业务层处理
 *
 * @author ruoyi
 * @date 2024-01-11
 */
@RequiredArgsConstructor
@Service
public class HiasEventServiceImpl implements IHiasEventService {

    private final HiasEventMapper baseMapper;
    private final HiasEventTagMapper eventTagMapper;
    private final HiasEventClerkMapper eventClerkMapper;
    private final HiasClerkMapper clerkMapper;
    private final EventRepository eventRepository;
    //private static ReentrantReadWriteLock.ReadLock readLock;
    //private static ReentrantReadWriteLock.WriteLock writeLock;

    private static RReadWriteLock lock;
    private static RLock readLock;
    private static RLock writeLock;

    private final RabbitTemplate rabbitTemplate;
    @Value("活动通知交换机")
    private String exchangeName;


    private final RedissonClient CLIENT = SpringUtils.getBean(RedissonClient.class);

    static {
        RedissonClient redissonClient = SpringUtils.getBean(RedissonClient.class);
        lock = redissonClient.getReadWriteLock("EventLock");
        readLock = lock.readLock();
        writeLock = lock.writeLock();
    }

    //private static ReentrantLock lock = new ReentrantLock();
    //key value
    private static Map<Long, Long> maxMap = new HashMap<>();
    private static Map<Long, Long> countMap = new HashMap<>();
    private static Map<Long, Long> putMap = new HashMap<>();
    private static Map<Long, Long> expireMap = new HashMap<>();
    private static Map<Long, Long> safeCountMap = new ConcurrentHashMap<>();



//    static {
//        ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
//        readLock = lock.readLock();
//        writeLock = lock.writeLock();
//    }

    /**
     * 查询【请填写功能名称】
     */
    @Override
    public HiasEventVo queryById(Long id, Long clerkId){
        //该用户是否报名过这个活动
        QueryWrapper<HiasEventClerk> wrapper = new QueryWrapper();
        wrapper.eq(clerkId != null, "clerk_id", clerkId);
        wrapper.eq(id != null, "id", id);
        wrapper.eq("check_status", 1);
        Long result = eventClerkMapper.selectCount(wrapper);

        HiasEventVo vo = baseMapper.selectVoById(id);
        vo.setUStatus(1);
        if(result == 0){
            vo.setUStatus(0);
        }
        return vo;

    }

    /**
     * 查询【请填写功能名称】列表
     */
    @Override
    public TableDataInfo<HiasEventVo> queryPageList(HiasEventBo bo, PageQuery pageQuery) {
        LambdaQueryWrapper<HiasEvent> lqw = buildQueryWrapper(bo);
        Page<HiasEventVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
        return TableDataInfo.build(result);
    }

    /**
     * 查询【请填写功能名称】列表
     */
    @Override
    public List<HiasEventVo> queryList(HiasEventBo bo) {
        LambdaQueryWrapper<HiasEvent> lqw = buildQueryWrapper(bo);
        return baseMapper.selectVoList(lqw);
    }

    private LambdaQueryWrapper<HiasEvent> buildQueryWrapper(HiasEventBo bo) {
        Map<String, Object> params = bo.getParams();
        LambdaQueryWrapper<HiasEvent> lqw = Wrappers.lambdaQuery();
        lqw.like(StringUtils.isNotBlank(bo.getEventName()), HiasEvent::getEventName, bo.getEventName());
        lqw.eq(StringUtils.isNotBlank(bo.getLocation()), HiasEvent::getLocation, bo.getLocation());
        lqw.eq(bo.getStartTime() != null, HiasEvent::getStartTime, bo.getStartTime());
        lqw.eq(bo.getEndTime() != null, HiasEvent::getEndTime, bo.getEndTime());
        lqw.eq(bo.getRegisterDeadline() != null, HiasEvent::getRegisterDeadline, bo.getRegisterDeadline());
        lqw.eq(bo.getMax() != null, HiasEvent::getMax, bo.getMax());
        lqw.eq(StringUtils.isNotBlank(bo.getIntro()), HiasEvent::getIntro, bo.getIntro());
        lqw.eq(StringUtils.isNotBlank(bo.getBanner()), HiasEvent::getBanner, bo.getBanner());
        lqw.eq(bo.getMasterId() != null, HiasEvent::getMasterId, bo.getMasterId());
        lqw.eq(bo.getPoint() != null, HiasEvent::getPoint, bo.getPoint());
        return lqw;
    }

    /**
     * 新增【请填写功能名称】
     */
    @Override
    public Boolean insertByBo(HiasEventBo bo) {
        HiasEvent add = BeanUtil.toBean(bo, HiasEvent.class);
        validEntityBeforeSave(add);
        boolean flag = baseMapper.insert(add) > 0;
        if (flag) {
            bo.setId(add.getId());
        }

        for (int i = 0; i < bo.getTagId().size(); i++) {
            HiasEventTag item = new HiasEventTag();
            item.setEventId(add.getId());
            item.setTagId(bo.getTagId().get(i));
            eventTagMapper.insert(item);
        }



        return flag;
    }

    /**
     * 修改【请填写功能名称】
     */
    @Override
    public Boolean updateByBo(HiasEventBo bo) {
        HiasEvent update = BeanUtil.toBean(bo, HiasEvent.class);
        validEntityBeforeSave(update);
        return baseMapper.updateById(update) > 0;
    }

    /**
     * 保存前的数据校验
     */
    private void validEntityBeforeSave(HiasEvent entity){
        //TODO 做一些数据校验,如唯一约束
    }

    /**
     * 批量删除【请填写功能名称】
     */
    @Override
    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
        if(isValid) {
            //TODO 做一些业务上的校验,判断是否需要校验
        }
        return baseMapper.deleteBatchIds(ids) > 0;
    }

    @Override
    public String join(HiasEventClerkBo bo) {

        //活动是否存在
//        QueryWrapper<HiasEvent> wrapper = new QueryWrapper<>();
//        wrapper.eq(bo.getEventId() != null, "id",bo.getEventId());
//        boolean eventexist = baseMapper.exists(wrapper);
//        if(eventexist){
//            //活动上限人数
//            Long max = 0L;
//            if(maxMap.containsKey(bo.getEventId())) {
//                max = maxMap.get(bo.getEventId());
//            }else {
//                HiasEvent event = baseMapper.selectById(bo.getEventId());
//                maxMap.put(bo.getEventId(), event.getMax());
//                max = event.getMax();
//            }
//
//            //活动已有人数
//            Long nowCount = 0L;
//            try {
//                readLock.lock();
//                if(countMap.containsKey(bo.getEventId())) {
//                    nowCount = countMap.get(bo.getEventId());
//                }else {
//                    QueryWrapper<HiasEventClerk> clerkQueryWrapper = new QueryWrapper<>();
//                    clerkQueryWrapper.eq("event_id", bo.getEventId());
//                    nowCount = eventClerkMapper.selectCount(clerkQueryWrapper);
//                    countMap.put(bo.getEventId(), nowCount);
//                }
//
//                if(max <= nowCount) {
//                    return "人数已经达到活动上限，报名失败";
//                }
//            } finally {
//                readLock.unlock();
//            }
//
//            boolean save = false;
//                writeLock.lock();
//                //再次检查人数是否到达上限
//                //readLock.lock();
//                Long clerkCount = countMap.get(bo.getEventId());
//                //readLock.unlock();
//                if(max > clerkCount) {
//                    save = true;
//                    //更新缓存中人数
//                    countMap.put(bo.getEventId(), clerkCount+1);
//                }
//                writeLock.unlock();
//                if(save) {
//                    HiasEventClerk eventClerk = new HiasEventClerk();
//                    eventClerk.setClerkId(bo.getClerkId());
//                    eventClerk.setEventId(bo.getEventId());
//                    eventClerk.setCheckStatus(0);
//                    eventClerkMapper.insert(eventClerk);
//                }
//                return "报名成功";
//        } else{
//            return "活动不存在！";
//        }
        //活动是否存在
        QueryWrapper<HiasEvent> wrapper = new QueryWrapper<>();
        wrapper.eq(bo.getEventId() != null, "id",bo.getEventId());
        boolean eventexist = baseMapper.exists(wrapper);

        if(eventexist) {
            //活动截止日期 expire time

            Long max = 0L;
            if(!maxMap.containsKey(bo.getEventId())) { //max有效
                HiasEvent event = baseMapper.selectById(bo.getEventId());
                maxMap.put(bo.getEventId(), event.getMax());
                putMap.put(bo.getEventId(), System.currentTimeMillis());
                expireMap.put(bo.getEventId(), 60000L);
                max = event.getMax();
            }else {
                max = maxMap.get(bo.getEventId());
            }

            QueryWrapper<HiasEventClerk> wrapper1 = new QueryWrapper<>();
            wrapper1.eq(bo.getEventId() != null, "event_id", bo.getEventId());
            wrapper1.eq("check_status", 3);
            Long clerkNum = 0L;
            readLock.lock();
            if(countMap.containsKey(bo.getEventId())) {
                //readLock.lock();
                clerkNum = countMap.get(bo.getEventId());
                //readLock.unlock();
            }else {
                //活动已通过报名的人数
                //writeLock.lock();
                clerkNum = eventClerkMapper.selectCount(wrapper1);
                countMap.put(bo.getEventId(), clerkNum);
                //writeLock.unlock();
            }
            readLock.unlock();

                boolean flag = false;
                if (clerkNum < max) {
                    //lock.lock();
                    writeLock.lock();
                    clerkNum = countMap.get(bo.getEventId());
                    if(clerkNum < max) {
                        System.out.println(Thread.currentThread().getName()+"clerkNum: "+clerkNum);
                        flag = true;
                        //clerkNum = eventClerkMapper.selectCount(wrapper1);
                        countMap.put(bo.getEventId(), clerkNum+1);
                    }
                    writeLock.unlock();
                    //lock.unlock();
                    if(flag) {
                        //创建一条报名记录
                        HiasEventClerk eventClerk = new HiasEventClerk();
                        eventClerk.setClerkId(bo.getClerkId());
                        eventClerk.setEventId(bo.getEventId());
                        eventClerk.setCheckStatus(0);
                        eventClerkMapper.insert(eventClerk);
                    }
                    return "申请成功！";
                }
        }

        //同步neo4j
        eventRepository.addRelation(bo.getClerkId(),bo.getEventId());

        return "OK";

    }

    public void ll(Long eventID) {
        while (true) {
            Long now = System.currentTimeMillis();
            Long old = putMap.get(eventID);
            if(now-old < expireMap.get(eventID)) {

            }else {
                maxMap.remove(eventID);
                //..
            }
        }
    }

    @Override
    public List<HiasEventClerk> applylist(Long eventId) {
        QueryWrapper<HiasEventClerk> wrapper = new QueryWrapper<>();
        wrapper.eq(eventId != null, "event_id", eventId);
        return eventClerkMapper.selectList(wrapper);

    }

    @Override
    public String checkApplication(Long id, int checkStaus) {
        if(id != null && checkStaus > 0 && checkStaus < 3){
            HiasEventClerk application = eventClerkMapper.selectById(id);
            application.setCheckStatus(checkStaus);
            eventClerkMapper.updateById(application);
            return "审核完成！";
        }else {
            return "参数有误！";
        }
    }

    @Override
    public Map<Long, String> checkError() {
        Map<Long, String> result = new HashMap<>();
        List<HiasEvent> events = baseMapper.selectList();
        events.forEach(event -> {
            QueryWrapper<HiasEventClerk> clerkQueryWrapper = new QueryWrapper<>();
            clerkQueryWrapper.eq("event_id", event.getId());
            Long clerkNum = eventClerkMapper.selectCount(clerkQueryWrapper);
            //result.put(event)
            System.out.println(event.getId()+" 最大人数："+event.getMax()+" 报名人数："+clerkNum);
        });
        return null;
    }

    @Override
    public Boolean load() {
        List<HiasEventTO> eventTOList = new ArrayList<>();
        List<HiasEvent> eventList = baseMapper.selectList();
        for (int i = 0; i < eventList.size(); i++) {
            HiasEventTO eventTO = new HiasEventTO();
            QueryWrapper<HiasEventClerk> wrapper = new QueryWrapper<>();
            wrapper.eq("event_id",eventList.get(i).getId());
            List<HiasEventClerk> eventClerkList = eventClerkMapper.selectList(wrapper);
            eventTO.setId(eventList.get(i).getId());
            eventTO.setEventName(eventList.get(i).getEventName());
            eventTO.setEventClerkList(eventClerkList);
            eventTOList.add(eventTO);
        }

        for (int i = 0; i < eventTOList.size(); i++) {
            HiasEventTO to = eventTOList.get(i);
            Event event = new Event();
            event.setEventId(to.getId());
            event.setEventName(to.getEventName());
            List<Clerk> clerks = new ArrayList<>();
            for (int j = 0; j < to.getEventClerkList().size(); j++) {
                Clerk clerk = new Clerk();
                clerk.setClerkId(to.getEventClerkList().get(j).getClerkId());
                clerks.add(clerk);
            }
            event.setEventClerkList(clerks);
            eventRepository.save(event);
        }




        return null;
    }

    @Override
    public List<HiasEventHotVo> hot(Long clerkId, Long eventId) {
        List<HiasEventHotTO> tos = eventRepository.hot(clerkId, eventId);
        List<HiasEventHotVo> vos = new ArrayList<>();
        for (int i = 0; i < tos.size(); i++) {
            HiasEventHotVo eventHotVo = new HiasEventHotVo();
            eventHotVo.setId(tos.get(i).getEventId());
            eventHotVo.setEventName(baseMapper.selectById(tos.get(i).getEventId()).getEventName());
            eventHotVo.setBanner(baseMapper.selectById(tos.get(i).getEventId()).getBanner());
            vos.add(eventHotVo);
        }

        return vos;
    }

    @Override
    public R eventDelay(HiasEventDelayBo bo) {
        QueryWrapper<HiasEventClerk> wrapper = new QueryWrapper<>();
        wrapper.eq("event_id", bo.getEventId());
        wrapper.eq("check_status", 1);
        List<HiasEventClerk> eventClerkList = eventClerkMapper.selectList(wrapper);

       HiasEvent event = baseMapper.selectById(bo.getEventId());
       event.setStartTime(bo.getDelayStartDate());
       event.setEndTime(bo.getDelayEndDate());

        HiasEventDelayMessage eventDelayMessage = new HiasEventDelayMessage();
        eventDelayMessage.setEventID(String.valueOf(bo.getEventId()));
        eventDelayMessage.setMessageID(UUID.randomUUID().toString());
        eventDelayMessage.setType("2");
        eventDelayMessage.setForm(bo.getNoticeType());
        SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        eventDelayMessage.setMsg("活动将延期举行，时间改为从"+formatter.format(bo.getDelayStartDate())+"到"+formatter.format(bo.getDelayEndDate()));
        String delayMessage = eventDelayMessage.toJsonString();
        String routingKey = null;
        if("mail".equals(bo.getNoticeType())){
            routingKey = "hias.mail";
        }else if("message".equals(bo.getNoticeType())){
            routingKey = "hias.info";
        }else if("wx".equals(bo.getNoticeType())){
            routingKey = "hias.wx";
        }else {        }
        rabbitTemplate.convertAndSend(exchangeName, routingKey, delayMessage);

        return R.ok();
    }

    @RabbitListener(queues = {"短信通知"})
    public void infoConsumer(String message) {
        HiasEventBaseMessage baseMessage =  HiasEventBaseMessage.toMessage(message);
        baseMessage.setForm("message");
        baseMessage.doLogic(baseMapper, eventClerkMapper, clerkMapper);
    }

    @RabbitListener(queues = {"邮件通知"})
    public void mailConsumer(String message) {
        HiasEventBaseMessage baseMessage =  HiasEventBaseMessage.toMessage(message);
        baseMessage.setForm("mail");
        baseMessage.doLogic(baseMapper, eventClerkMapper, clerkMapper);
//        System.out.println(message);
    }

    @RabbitListener(queues = {"微信公众号通知"})
    public void wxConsumer(String message) {
        System.out.println(message);
    }

}
